#include <stdlib.h>
#include <string.h>
#include <directfb.h>
#include <direct/debug.h>

#include "dtk_types.h"
#include "dtk_debug.h"
#include "dtk_priv.h"
#include "dtk_actor.h"
#include "dtk_texture.h"

#define DTK_TYPE_TEXTURE "DTKTexture"

void texture_draw(DTKActor* self, DTKGC* gc, DRegion* region)
{
    IDirectFBSurface* surface = NULL;
    DTKTexture* texture = (DTKTexture*) self;
    DFBRectangle source_rect;
    DFBRectangle destination_rect;
    DFBResult res;
    DBoolean ret;

    DTK_TYPE_ASSERT (self, DTK_TYPE_TEXTURE);
    if (gc == NULL)
    {
        return;
    }

    dtk_region_dump (__func__, region);

    surface = gc->surface;

    source_rect.x = 0;
    source_rect.y = 0;
    source_rect.w = texture->width;
    source_rect.h = texture->height;

    destination_rect.x = region->x1;
    destination_rect.y = region->y1;
#if 0
    ret = dtk_layout_get_fix_size (dtk_actor_get_layout (self), 
                        &destination_rect.w,
                        &destination_rect.h);
    if (ret == DTK_FALSE)
    {
        destination_rect.w = region->x2 - region->x1;
        destination_rect.h = region->y2 - region->y1;
    }
#else
    destination_rect.w = region->x2 - region->x1;
    destination_rect.h = region->y2 - region->y1;
#endif
    
    res = surface->StretchBlit (surface, texture->surface,
        &source_rect, &destination_rect);
    if (res != DFB_OK)
    {
        DInt width, height;
        DFBRegion region;

        surface->GetSize (surface, &width, &height);
        printf ("res = %d, (%d, %d)\n", res, width, height);
        res = surface->GetClip (surface, &region);        
        printf ("res = %d, (%d, %d)-(%d, %d, %d, %d)\n",
            res, width, height, region.x1, region.y1, region.x2, region.y2);
    }

    return;    
}

static void texture_update (DTKLayout* self)
{
    DTKActor* actor;
    DTKTexture* texture;
    DInt width, height;

    actor = dtk_layout_get_actor(self);
    DTK_TYPE_ASSERT(actor, DTK_TYPE_TEXTURE);
    texture = (DTKTexture*) actor;

    if (DTK_FALSE == dtk_layout_get_fix_size(self, &width, &height))
    {
        texture->surface->GetSize (texture->surface, &width, &height);
        dtk_layout_set_default_size (self, width, height);
    }
        
    return;
}

void texture_release (DTKActor* self)
{
    DTKTexture* texture = (DTKTexture*) self;
    DTK_TYPE_ASSERT (self, DTK_TYPE_TEXTURE);

    if (texture->surface)
    {
        texture->surface->Release (self);
    }
   
    return;
}

DTKActor* dtk_texture_new (void)
{
    DTKActor*               actor;
    DTKActorCallback*       funcs;
    DTKLayout*              layout;

    actor = dtk_actor_init (sizeof(DTKTexture), DTK_TYPE_TEXTURE, DACAPS_NONE);
    funcs = DTK_ACTOR_CALLBACK(actor);

    funcs->draw = texture_draw;
    layout = dtk_actor_get_layout(actor);
    layout->update = texture_update;
    funcs->release = texture_release;

    return actor;
}

DBoolean dtk_texture_set_from_file(DTKActor* self,
                    DString filename)
{
    DTKTexture* texture = (DTKTexture*) self;
    DFBResult ret;
    IDirectFBImageProvider* provider = NULL;
    DFBSurfaceDescription dsc;
    IDirectFB* dfb;
    DInt width, height;

    DTK_TYPE_ASSERT (self, DTK_TYPE_TEXTURE);

    if (texture->surface)
    {
        texture->surface->Release (texture->surface);
        texture->surface = NULL;
    }

    dfb = dtk_get_dfb();
    ret = dfb->CreateImageProvider(dfb, filename, &provider);
    if (ret != DFB_OK)
    {
        D_DERROR(ret,
               "CreateImageProvider for '%s' failed\n",
               filename);
        return DTK_FALSE;
    }

    ret = provider->GetSurfaceDescription(provider, &dsc);
    if (ret)
    {
        D_DERROR(ret, "load_image: GetSurfaceDescription %dx%d failed\n",
                   dsc.width, dsc.height);
        provider->Release(provider);
        return DTK_FALSE;
    }

    ret = dfb->CreateSurface(dfb, &dsc, &texture->surface);
    if (ret)
    {
        D_DERROR(ret, "load_image: CreateSurface %dx%d failed\n",
                   dsc.width, dsc.height);
        provider->Release(provider);
    }

    /* Render the image to the created surface */
    ret = provider->RenderTo(provider, texture->surface, NULL);
    if (ret)
    {
        D_DERROR(ret,
                 "load_image: RenderTo for '%s': %s\n",
                 filename, DirectFBErrorString(ret));
        texture->surface->Release(texture->surface);
        provider->Release(provider);
        return ret;
    }

    texture->width = dsc.width;
    texture->height = dsc.height;
    dtk_actor_set_default_size (self, dsc.width, dsc.height);

    provider->Release(provider);

    return DTK_TRUE;
}

DTKActor* dtk_texture_new_from_file (const DString filename)
{
    DTKActor* texture = dtk_texture_new();
    DBoolean ret;

    if (texture == NULL)
    {
        return NULL;
    }

    ret = dtk_texture_set_from_file(DTK_TEXTURE(texture), filename);
    if (ret == DTK_FALSE)
    {
        dtk_actor_finalize(texture);
        texture = NULL;
    }

    return texture;
}
